package com.cmf.exception;

import com.cmf.common.Result;
import com.cmf.enums.ResultCodeType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.validation.BindException;

import javax.validation.ConstraintViolationException;
import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局异常处理器
 * 用于统一处理应用程序中抛出的各种异常，将其转换为友好的响应格式
 */
@Slf4j  // Lombok注解，自动注入日志对象
@RestControllerAdvice  // 标识这是一个全局异常处理类，并自动将返回值转换为JSON格式
public class GlobalExceptionHandler {


    /**
     * 处理业务异常
     * 用于处理业务逻辑中主动抛出的BusinessException异常
     *
     * @param e 业务异常对象
     * @return 统一响应对象
     */
    @ExceptionHandler(BusinessException.class)  // 指定要处理的异常类型
    public Result<Void> handleBusinessException(BusinessException e) {
        // 记录错误日志
        log.error("业务异常：{}", e.getMessage());
        // 返回错误响应
        return Result.error(e.getCode(), e.getMessage());
    }

    /**
     * 处理参数校验异常
     * 用于处理@Valid注解验证失败抛出的异常
     * 常见于@RequestBody注解的对象参数校验
     *
     * @param e 参数校验异常对象
     * @return 统一响应对象
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValidException(MethodArgumentNotValidException e) {
        // 获取校验结果
        BindingResult bindingResult = e.getBindingResult();
        // 获取第一个错误信息
        String message = bindingResult.getAllErrors().get(0).getDefaultMessage();
        // 记录错误日志
        log.error("参数校验异常：{}", message);
        // 返回400错误响应
        return Result.error( ResultCodeType.PARAM_MISSING, message);
    }

    /**
     * 处理参数绑定异常
     * 用于处理GET请求中的参数绑定错误
     * 常见于@RequestParam注解的参数绑定失败
     *
     * @param e 参数绑定异常对象
     * @return 统一响应对象
     */
    @ExceptionHandler(BindException.class)
    public Result<Void> handleBindException(BindException e) {
        // 获取第一个错误信息
        String message = e.getAllErrors().get(0).getDefaultMessage();
        // 记录错误日志
        log.error("参数绑定异常：{}", message);
        // 返回400错误响应
        return Result.error(ResultCodeType.PARAM_MISSING, message);
    }

    /**
     * 处理约束违反异常
     * 用于处理javax.validation包中的注解验证失败
     * 常见于@NotNull、@Size等注解的验证失败
     *
     * @param e 约束违反异常对象
     * @return 统一响应对象
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Void> handleConstraintViolationException(ConstraintViolationException e) {
        // 获取错误信息
        String message = e.getMessage();
        // 记录错误日志
        log.error("约束违反异常：{}", message);
        // 返回400错误响应
        return Result.error(ResultCodeType.PARAM_MISSING, message);
    }

    /**
     * 处理重复键异常
     * 用于处理数据库中违反唯一约束的情况
     *
     * @param e 重复键异常对象
     * @return 统一响应对象
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public Result<Void> handleDuplicateKeyException(DuplicateKeyException e) {
        log.error("重复键异常：", e);
        return Result.error(ResultCodeType.DATA_ALREADY_EXIST, "数据已存在");
    }

    /**
     * 处理SQL完整性约束违反异常
     * 用于处理数据库中违反完整性约束的情况
     *
     * @param e SQL完整性约束违反异常对象
     * @return 统一响应对象
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public Result<Void> handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
        log.error("SQL完整性约束违反异常：", e);
        return Result.error(ResultCodeType.DATA_ALREADY_EXIST, "数据已存在");
    }

    /**
     * 处理其他未知异常
     * 用于处理所有未被上述处理器捕获的异常
     * 作为最后的防护网，确保所有异常都能被处理
     *
     * @param e 异常对象
     * @return 统一响应对象
     */
    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        // 记录错误日志，包含完整的堆栈信息
        log.error("系统异常：", e);
        // 返回500错误响应，不暴露具体错误信息
        return Result.error(ResultCodeType.INTERNAL_SERVER_ERROR);
    }

}